<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app"></div>
<script src="../node_modules/@vue/reactivity/dist/reactivity.global.js"></script>

<script>
  const { effect,reactive } = VueReactivity
  console.log('reactive({name:\'ahhh\',age:123,other:\'xxx\',arr:[1,2,3]})');
  const state = reactive({name:'ahhh',age:123,other:'xxx',arr:[1,2,3]})

  /** case1-2
   * 1.触发setter不会触发getter
   * 2.触发setter前会触发getter*/
  /*const fn = () => {
    console.log('render')
    app.innerHTML = state.name + ':' + state.age;
  };
  console.log(fn.toString())
  effect(fn);
  setTimeout(() => {
    console.log('==================================')
    console.log('准备改变数据')

    /!** case1 触发setter不会触发getter*!/
    //赋值操作不会触发getter 只会触发setter ---> trigger
    /!*console.log('state.name = \'rocky\'');
    state.name = 'rocky'; //{name: "ahhh", age: 123, other: "xxx", arr: Array(3)} "set" "name" "rocky" "ahhh"*!/

    //新增也不会触发getter 只会触发setter
    /!*console.log('state.AAA = \'aaa\'');
    state.AAA = 'aaa'; //{name: "ahhh", age: 123, other: "xxx", arr: Array(3)}"add" "AAA" "aaa" undefined*!/

    /!** case2 触发setter前会触发getter*!/
    //访问state.arr会取一次值, 会触发state的getter,
    //state.arr是个对象, 会调用reactive对其进行递归代理
    //这样再 state.arr[2] = 33 的赋值操作就会执行刚递归代理出来的setter ---> trigger
    console.log('state.arr[2] = 33');
    state.arr[2] = 33; //[1, 2, 3] "set" "2" 33 3

    // console.log('state.arr[9] = 4');
    // state.arr[9] = 4; //[1, 2, 3] "add" "9" 4 undefined
    // console.log('state.arr.length = 100');
    // state.arr.length = 100; //[1, 2, 3] "set" "length" 100 3
  }, 1000)*/


  /** case3 state.arr.push
   *  会触发2次getter2次track(push和length) 和 2次setter1次trigger(index)*/
  const fn = () => {
    console.log('render')
    // app.innerHTML = state.name + ':' + state.age;
    app.innerHTML = state.arr
  };
  console.log(fn.toString())
  effect(fn);
  setTimeout(()=>{
    console.log('==================================')
    console.log('准备改变数据')

    /** case3 state.arr.push
     *  会触发2次getter2次track(push和length) 和 2次setter1次trigger(index)*/
    //push的时候
    //先会对push取值 然后对length取值
    //然后再调用arr的setter 会先设置 index->value 再设置 length->index+1
    /*
      准备改变数据
      state.arr.push('push elm')
      正在对代理对象:{"name":"ahhh","age":123,"other":"xxx","arr":[1,2,3]}的arr取值,effect()返回的effect准备收集为属性arr的响应式依赖,一旦代理对象的arr属性的值发生变动 就会重新执行收集到的对应的effect
      当前正在运行的effect activeEffect(即要收集的effect)为: undefined
      正在懒代理属性对象arr
      existingProxy: false
      正在对代理对象:[1,2,3]的push取值,effect()返回的effect准备收集为属性push的响应式依赖,一旦代理对象的就会重新执行收集到的对应的effect
      当前正在运行的effect activeEffect(即要收集的effect)为: undefined
      正在对代理对象:[1,2,3]的length取值,effect()返回的effect准备收集为属性length的响应式依赖,一旦代理对象的length属性的值发生变动 就会重新执行收集到的对应的effect
      当前正在运行的effect activeEffect(即要收集的effect)为: undefined

      set target:[1,2,3] key:3 value:111
      oldValue:undefined,newValue:111
      trigger: (4)[1, 2, 3, 111] set类型为:add 改变的属性是:3 新值为:111 老值为:undefined //对应补丁2
      此时就会让effect重新执行 （app.innerHTML = state.arr 时会监控length属性(即length上有这个effect)，我们这里是知道是push方法调用, 于是直接就让它找length绑定的effect, 具体查看 补丁2: 给数组一个不存在的索引位置上赋值也需要触发更新）

      set target:[1,2,3,111] key:length value:4
      oldValue:4,newValue:4
      新老值一样 不会再次触发trigger
      故不用担心 arr.push 会触发两次trigger
    */
    console.log('state.arr.push(\'push elm\')');
    state.arr.push(111);
  }, 1000)


  /** 补丁1： 对修改的是数组长度的情况打补丁(hack)
   * 不论期初是否监控了数组的length属性, 当我们改变数组的length属性时都需要判断一下是否需要更新*/
  /*const fn = () => {
    console.log('render')

    // app.innerHTML = state.arr.length; //情况一
    app.innerHTML = state.arr[2]; //情况二
    // app.innerHTML = state.arr[2] + state.arr.length; //情况三
  };
  console.log(fn.toString())
  effect(fn);
  setTimeout(()=>{
    console.log('state.arr.length = 1;');
    state.arr.length = 1; //情况二 vue3中 表现为 修改视图为 undefined (因为state.arr[2]因为索引变短已经不存在了,但如果不是将length变小而是变大,是不会更新视图的)
    //state.arr.length = 111; //这样的话 情况二 不会更新

    //todo 这样会改变两次值（相当于两次更新） 会执行两次effect
    /!*state.arr.length = 1;
    state.arr.length = 3;*!/
  },1000)*/

  /** 补丁2: 给数组一个不存在的索引位置上赋值也需要触发更新*/
  /*const fn = () => {
    console.log('render')

    app.innerHTML = state.arr; //这样赋值给app.innerHTML 会调用stringify 会读取state.arr上所有属性 包括.length
  };
  console.log(fn.toString())
  effect(fn);
  setTimeout(()=>{
    state.arr[100] = 1;
  },1000)*/


  /** case： 不会更新*/
  /*effect(() => {
    console.log('render')
    app.innerHTML = state.name + ',' + state.age
  });
  setTimeout(()=>{
    state.whatever = 'xxx'; //给响应式对象赋一个新属性 是不会触发更新的
  },2000)*/

  /** 补丁3: 会更新*/
  /*effect(() => {
    console.log('render')
    // app.innerHTML = state; //情况1
    app.innerHTML = JSON.stringify(state) //情况2
  });
  setTimeout(()=>{
    state.whatever = 'xxx'; //如果是情况2 此时会触发更新 (情况1不会)
  },2000)*/
</script>
</body>
</html>
